package com.xishu.service;

import com.alibaba.fastjson.JSON;
import com.xishu.bo.AccountExistResult;
import com.xishu.bo.SmsCount;
import com.xishu.bo.TicketAndLimit;
import com.xishu.client.CustomerEmailClient;
import com.xishu.client.EmailClient;
import com.xishu.client.sms.AliyunSMSClient;
import com.xishu.client.sms.SmsFactory;
import com.xishu.config.Config;
import com.xishu.entity.AbstractEntity;
import com.xishu.entity.Company;
import com.xishu.entity.NameAndId;
import com.xishu.entity.User;
import com.xishu.entity.customer.*;
import com.xishu.entity.order.Order;
import com.xishu.entity.order.OrderItem;
import com.xishu.entity.plat.GlobalSetting;
import com.xishu.entity.shop.CompanyMainItem;
import com.xishu.entity.shop.DiscountTicket;
import com.xishu.entity.shop.Shop;
import com.xishu.exception.ResponseStatusException;
import com.xishu.response.VerifyUtil;
import com.xishu.service.order.base.OrderService;
import com.xishu.util.*;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;

import java.io.File;
import java.text.ParseException;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static com.xishu.config.Constant.*;
import static com.xishu.response.ResponseStatus.*;

/**
 * 会员系统服务
 * <p>
 * phone email nickname sex birthday age preference renewurl
 */
public class CustomerService implements Tools {
    private static Logger logger = LoggerFactory.getLogger(CustomerService.class);

    private static CustomerService instance = new CustomerService();
    private static GlobalService globalService = GlobalService.getInstance();
    private static final String CUSTOMER_EXPIRE_TIME = "customer.expire.time.";
    public static final String PHONE_AREA_CODE = "phone.area.code.";
    private CommonService commonService = CommonService.getInstance();
    private CompanyService companyService = CompanyService.getInstance();
    public static Map<Integer, String> PREFERENCE_MAP_ZH;
    public static Map<Integer, String> PREFERENCE_MAP_EN;
    public static Map<Long, String> SMS_TEMPLATE_MAP;
    public static Map<Long, String> SMS_TEMPLATE_MAP_ZH;
    public static Map<Long, String> SMS_TEMPLATE_MAP_EN;


    {
        PREFERENCE_MAP_ZH = new HashMap();
        PREFERENCE_MAP_ZH.put(1, "中菜");
        PREFERENCE_MAP_ZH.put(2, "粵菜");
        PREFERENCE_MAP_ZH.put(3, "臺灣菜");
        PREFERENCE_MAP_ZH.put(4, "日本菜");
        PREFERENCE_MAP_ZH.put(5, "韓國菜");
        PREFERENCE_MAP_ZH.put(6, "泰國菜");
        PREFERENCE_MAP_ZH.put(7, "亞州菜");
        PREFERENCE_MAP_ZH.put(8, "意大利菜");
        PREFERENCE_MAP_ZH.put(9, "法國菜");
        PREFERENCE_MAP_ZH.put(10, "西餐");

        PREFERENCE_MAP_EN = new HashMap();
        PREFERENCE_MAP_EN.put(1, "Chinese");
        PREFERENCE_MAP_EN.put(2, "Cantonese");
        PREFERENCE_MAP_EN.put(3, "Taiwansee");
        PREFERENCE_MAP_EN.put(4, "Japanese");
        PREFERENCE_MAP_EN.put(5, "Korean");
        PREFERENCE_MAP_EN.put(6, "Thai");
        PREFERENCE_MAP_EN.put(7, "Asian");
        PREFERENCE_MAP_EN.put(8, "Italin");
        PREFERENCE_MAP_EN.put(9, "French");
        PREFERENCE_MAP_EN.put(10, "Western");

        SMS_TEMPLATE_MAP = new HashMap<>();
        SMS_TEMPLATE_MAP_ZH = new HashMap<>();
        SMS_TEMPLATE_MAP_EN = new HashMap<>();

        SMS_TEMPLATE_MAP.put(1l, "%s尊敬的會員您好，送你一張優惠券%s，有效期%s。該優惠券已存入您的賬號。Dear member, you have a new coupon %s saved into your account, valid period %s. Please visit us again to redeem it!");
        SMS_TEMPLATE_MAP.put(2l, "%s尊敬的會員您好，恭喜你收到一張優惠券%s。有效期%s，憑電子消費券於指定餐廳就餐即享優惠。Dear member, congratulations that you received a new  %s coupon. Valid period %s. Please visit applicable branches to enjoy the discounts.");
        SMS_TEMPLATE_MAP.put(3l, "%s恭喜您成功獲得本店%s優惠券。有效期%s，請再次光顧享受優惠。Dear member, congratulations that you received a new  %s coupon. Valid period %s. Please visit applicable branches to enjoy the discounts.");
        SMS_TEMPLATE_MAP.put(4l, "%s恭喜您已成功領取%s優惠券。有效期%s，請到相應門店登入賬戶享受優惠。Dear member, congratulations that you received a new  %s coupon. Valid period %s. Please visit applicable branches to enjoy the discounts.");
        SMS_TEMPLATE_MAP.put(5l, "%s憑電子消費券%s於指定餐廳就餐即享優惠。有效期%s。Redeem your discounts at assigned branches with  %s. Valid period %s.");

        SMS_TEMPLATE_MAP_ZH.put(1l, "%s尊敬的會員您好，送你一張優惠券%s，有效期%s。該優惠券已存入您的賬號。");
        SMS_TEMPLATE_MAP_ZH.put(2l, "%s尊敬的會員您好，恭喜你收到一張優惠券%s。有效期%s，憑電子消費券於指定餐廳就餐即享優惠。");
        SMS_TEMPLATE_MAP_ZH.put(3l, "%s恭喜您成功獲得本店%s優惠券。有效期%s，請再次光顧享受優惠。");
        SMS_TEMPLATE_MAP_ZH.put(4l, "%s恭喜您已成功領取%s優惠券。有效期%s，請到相應門店登入賬戶享受優惠。");
        SMS_TEMPLATE_MAP_ZH.put(5l, "%s憑電子消費券%s於指定餐廳就餐即享優惠。有效期%s。");

        SMS_TEMPLATE_MAP_EN.put(1l, "%sDear member, you have a new coupon %s saved into your account, valid period %s. Please visit us again to redeem it!");
        SMS_TEMPLATE_MAP_EN.put(2l, "%sDear member, congratulations that you received a new  %s coupon. Valid period %s. Please visit applicable branches to enjoy the discounts.");
        SMS_TEMPLATE_MAP_EN.put(3l, "%sDear member, congratulations that you received a new  %s coupon. Valid period %s. Please visit applicable branches to enjoy the discounts.");
        SMS_TEMPLATE_MAP_EN.put(4l, "%sDear member, congratulations that you received a new  %s coupon. Valid period %s. Please visit applicable branches to enjoy the discounts.");
        SMS_TEMPLATE_MAP_EN.put(5l, "%sRedeem your discounts at assigned branches with  %s. Valid period %s.");
    }

    private CustomerService() {
    }

    public static CustomerService getInstance() {
        return instance;
    }

    /**
     * 查询会员信息
     *
     * @param customerId
     * @return
     * @throws Exception
     */
    public Optional<Customer> findCustomerOptional(Long customerId) throws Exception {
        Optional<Customer> customerOptional = commonService.searchOneOptional(customerId, Customer.class);

        return customerOptional;
    }

    /**
     * 通过名称查询会员信息
     *
     * @param name
     * @return
     */
    public Customer findCustomerByName(String name, Long companyId) {
        Customer customer = new Customer();
        customer.setPhoneOrEmailOrCode(name);
        customer.setCompanyId(companyId);
        customer.setSortField(new String[]{"createTime"});
        customer.setSortType("DESC");
        return commonService.searchOne(customer);
    }

    /**
     * 通过邮件来查询会员信息
     *
     * @param email
     * @return
     */
    public Customer findCustomerByEmail(String email, Long companyId) {
        Customer customer = new Customer();
        customer.setEmail(email);
        customer.setCompanyId(companyId);
        return commonService.searchOne(customer);
    }

    /**
     * 通过电话号码来查询会员信息
     *
     * @param phone
     * @return
     */
    public Customer findCustomerByPhone(String phone, Long companyId) {
        Customer customer = new Customer();
        customer.setPhone(phone);
        customer.setCompanyId(companyId);
        return commonService.searchOne(customer);
    }

    /**
     * 通过电话号码来查询会员信息
     *
     * @param phone
     * @param areaCode
     * @param companyId
     * @return
     */
    public Customer findCustomerByPhone(String phone, String areaCode, Long companyId) {
        Customer customer = new Customer();
        customer.setPhone(phone);
        customer.setCompanyId(companyId);
        customer.setAreaCode(areaCode);
        return commonService.searchOne(customer);

    }

    /**
     * 查询会员
     *
     * @param customerId
     * @return
     */
    public Customer findCustomer(Long customerId) throws Exception {
        return commonService.findEntity(customerId, Customer.class);
    }

    /**
     * 查询会员列表
     *
     * @param customerIdList
     * @return
     */
    public List<Customer> findCustomerList(List<Long> customerIdList) {
        Customer customer = new Customer();
        customer.setIds(customerIdList);
        return commonService.searchAll(customer);
    }

    /**
     * 针对该餐厅下面的匹配的账号所做的操作
     *
     * @param companyId
     * @param vip
     * @param consumer
     */
    public void allCustomerAction(Long companyId, Boolean vip, Consumer<Customer> consumer) {
        int pageNumber = 1;

        Customer customer = new Customer();
        customer.setPageNumber(pageNumber);
        customer.setPageSize(100);
        customer.setCompanyId(companyId);
        customer.setVip(vip);

        while (true) {
            Page<Customer> search = commonService.search(customer);
            List<Customer> customerList = search.getContent();

            //没有找到，那么结束操作
            if (isEmpty(customerList)) {
                break;
            }

            pageNumber = pageNumber + 1;

            for (Customer customerInDb : customerList) {
                consumer.accept(customerInDb);
            }
        }
    }

    /**
     * 会员账号登录
     *
     * @param customerInDb
     * @param reqCustomer
     * @return
     */
    public boolean login(Customer customerInDb, Customer reqCustomer) {
        String password = customerInDb.getPassword();
        if (isEmpty(password)) {
            logger.info("password in db is null");
            return false;
        }

        String salt = customerInDb.getSalt();
        if (isEmpty(salt)) {
            logger.info("salt in db is null");
            return false;
        }

        String reqPassword = reqCustomer.getPassword();
        if (isEmpty(reqPassword)) {
            logger.info("req password is null");
            return false;
        }

        String calPassword = Sha256Util.sha256(reqPassword, salt);
        return calPassword.equals(password);
    }

    /**
     * 生成验证码，验证码是4位
     *
     * @param phoneOrEmail
     */
    public String generateRandom(String phoneOrEmail) {
        String numberCode = RandomUtil.generateNumberCode(4);
        JRedisUtil.setKV(CUSTOMER_EXPIRE_TIME + phoneOrEmail, numberCode);

        //设置失效时间，统一10分钟
        JRedisUtil.expire(CUSTOMER_EXPIRE_TIME + phoneOrEmail, Config.getInstance().customerCodeValidTime());
        logger.info("the random code is {}", numberCode);

        return numberCode;
    }

    /**
     * 获取缓存里面的验证码
     *
     * @param phoneOrEmail
     * @return
     */
    public String getRandomFromCache(String phoneOrEmail) {
        return JRedisUtil.getKV(CUSTOMER_EXPIRE_TIME + phoneOrEmail);
    }

    /**
     * 删除缓存里面的验证码
     *
     * @param phoneOrEmail
     */
    public void cleanRandomFromCache(String phoneOrEmail) {
        JRedisUtil.delKV(CUSTOMER_EXPIRE_TIME + phoneOrEmail);
    }

    /**
     * 生成短信验证码内容
     *
     * @param areaCode
     * @param numberCode
     * @return
     */
    public String smsRandomCodeContent(String areaCode, String numberCode) {
        if (SmsFactory.getInstance().isBeijingPhone(areaCode)) {
            return String.format("Your verification code is: %s, valid for 10 minutes.", numberCode);
        } else {
            return String.format("[DoLA] Your verification code is: %s, valid for 10 minutes.", numberCode);
        }
    }

    /**
     * 生成邮件验证码内容
     *
     * @param numberCode
     * @return
     */
    public String emailRandomCodeContent(String numberCode) throws Exception {

        GlobalSetting globalSetting = globalService.getGlobalSetting();
        String address = globalSetting.getAddress();
        String city = globalSetting.getCity();
        String companyWebsite = globalSetting.getCompanyWebsite();
        String emailAddress = globalSetting.getEmailAddress();
        String logoUrl = PlatService.getInstance().getLogoUrl();

        String content = String.format("<!DOCTYPE html>\n" +
                "<html lang=\"en\">\n" +
                "<head>\n" +
                "    <meta charset=\"UTF-8\">\n" +
                "    <title>Title</title>\n" +
                "</head>\n" +
                "<body>\n" +
                "<div style=\"padding-right: 10%%\" align=\"right\"><img style=\"width: 100px;height: 100px;\" src=\"%s\"></div>\n" +
                "<div style=\"padding-left: 10%%\">\n" +
                "    <div>Dear\n" +
                "        <div>\n" +
                "            <br/>\n" +
                "            Please use the verification code below to finish your registration process:\n" +
                "            <br/>\n" +
                "            <div style=\"color: #4793A3;text-decoration: none !important;text-align:center;font-weight:bold;font-size:xx-large \">%s</div>\n" +
                "            <br/>\n" +
                "            If you did not initiate this request, simply ignore this email.\n" +
                "            <br/>\n" +
                "            <br/>\n" +
                "            <br/>\n" +
                "        </div>\n" +
                "        <div align=\"center\">\n" +
                "            <div style=\"color: #808080\">" + address + "<br/>\n" +
                "               " + city + "<br/>\n" +
                "            </div>\n" +
                "            <div style=\"color: #4793A3;text-decoration: none !important;\">" + emailAddress + " |\n" +
                "                " + companyWebsite + "\n" +
                "            </div>\n" +
                "        </div>\n" +
                "    </div>\n" +
                "</div>\n" +
                "</body>\n" +
                "</html>\n", logoUrl, numberCode);

        return content;
    }

    /**
     * 验证码的标题
     *
     * @return
     */
    public String randomCodeEmailTitle(String numberCode) {
        return String.format("%s is your verification code", numberCode);
    }

    /**
     * 判断账号是否存在
     *
     * @param name
     * @return
     */
    public AccountExistResult isAccountExist(String name, Long companyId, boolean checkCustomerStatus) {
        AccountExistResult accountExistResult = new AccountExistResult();
        Customer customerByPhone = findCustomerByPhone(name, companyId);
        if (checkCustomerStatus) {
            //如果账号关停了，也认为账号不存在
            accountExistResult.setPhoneExist(customerByPhone != null && getInt(customerByPhone.getStatus()) == 0);
        } else {
            accountExistResult.setPhoneExist(customerByPhone != null);
        }

        Customer customerByEmail = findCustomerByEmail(name, companyId);
        if (checkCustomerStatus) {
            //如果账号关停了，也认为账号不存在
            accountExistResult.setEmailExist(customerByEmail != null && getInt(customerByEmail.getStatus()) == 0);
        } else {
            accountExistResult.setEmailExist(customerByEmail != null);
        }
        return accountExistResult;
    }

    /**
     * 判断账号是否存在
     *
     * @param customer
     * @param companyId
     * @return
     */
    public AccountExistResult isAccountExist(Customer customer, Long companyId) {
        AccountExistResult accountExistResult = new AccountExistResult();
        if (isNotEmpty(customer.getPhone())) {
            Customer customerByPhone = findCustomerByPhone(customer.getPhone(), customer.getAreaCode(), companyId);
            accountExistResult.setPhoneExist(customerByPhone != null);
        }

        if (isNotEmpty(customer.getEmail())) {
            Customer customerByEmail = findCustomerByEmail(customer.getEmail(), companyId);
            accountExistResult.setEmailExist(customerByEmail != null);
        }

        return accountExistResult;
    }

    /**
     * 是否包含VIP级别的等级
     *
     * @param vipLevelList
     * @return
     */
    public boolean hasVipLevel(List<VipLevel> vipLevelList) {
        return vipLevelList.stream().filter(v -> getBoolean(v.getVip())).findAny().isPresent();
    }

    /**
     * 创建VIP的会员等级
     *
     * @param companyId
     */
    public VipLevel createVipLevel(Long companyId) throws Exception {
        VipLevel vipLevel = new VipLevel();
        vipLevel.setCompanyId(companyId);
        vipLevel.setVip(true);
        commonService.createObject(vipLevel);
        return vipLevel;
    }


    /**
     * 查询某一个公司的配额
     *
     * @param companyId
     * @return
     */
    public Optional<Quota> findQuotaOrCreate(Long companyId) throws Exception {
        Quota quota = new Quota();
        quota.setCompanyId(companyId);
        Optional<Quota> quotaOptional = commonService.searchOneOptional(quota);

        if (!quotaOptional.isPresent()) {
            createQuoTa(companyId);
            return findQuotaOrCreate(companyId);
        }

        commonService.setDefaultValue(quota);
        //做可使用的处理
//        quota.setSmsCanUsed(quota.getSmsQuota() - quota.getSmsUsed());
//        quota.setEmailCanUsed(quota.getEmailQuota() - quota.getEmailUsed());
//        quota.setUnPlanSmsCanUsed(quota.getUnPlanSmsQuota() - quota.getUnPlanSmsUsed());
//        quota.setUnPlanEmailCanUsed(quota.getUnPlanEmailQuota() - quota.getUnPlanEmailUsed());

        return quotaOptional;
    }

    /**
     * 查詢配額
     *
     * @param quotaId
     * @return
     */
    public Quota findQuota(Long quotaId) throws Exception {
        Quota quota = commonService.findEntity(quotaId, Quota.class);
        commonService.setDefaultValue(quota);
        return quota;
    }

    /**
     * 创建某一个公司的配额
     *
     * @param companyId
     */
    public void createQuoTa(Long companyId) throws Exception {
        Quota quota = new Quota();
        quota.setCompanyId(companyId);
        commonService.createObject(quota);
    }

    /**
     * 查询VIP续费模式
     *
     * @param companyId
     * @return
     */
    public Optional<VipRenewMode> findVipRenewMode(Long companyId) {
        VipRenewMode vipRenewMode = new VipRenewMode();
        vipRenewMode.setEntity(true);
        vipRenewMode.setCompanyId(companyId);
        return commonService.searchOneOptional(vipRenewMode);
    }

    /**
     * 获取续费VIP的价格
     *
     * @param vipRenewMode
     * @return
     */
    public Double getRenewFee(VipRenewMode vipRenewMode) {
        if (showMoneyDouble(vipRenewMode.getRealCost()) > 0) {
            return vipRenewMode.getRealCost();
        }

        if (showMoneyDouble(vipRenewMode.getOriginCost()) > 0) {
            return vipRenewMode.getOriginCost();
        }

        return 0d;
    }

    /**
     * 查询用户对应的LEVEL级别
     *
     * @param vipLevelId
     * @return
     */
    public VipLevel findVipLevel(Long vipLevelId) throws Exception {
        return commonService.findEntity(vipLevelId, VipLevel.class);
    }

    /**
     * 查询用户对应的LEVEL级别
     *
     * @param vipLevelId
     * @return
     */
    public Optional<VipLevel> findVipLevelOption(Long vipLevelId) throws Exception {
        return commonService.searchOneOptional(vipLevelId, VipLevel.class);
    }

    /**
     * 通过公司来查询会员等级
     *
     * @param companyId
     * @return
     */
    public List<VipLevel> findVipLevelByCompany(Long companyId) throws ResponseStatusException {
        VerifyUtil.verify(() -> companyId != null);
        VipLevel vipLevel = new VipLevel();
        vipLevel.setCompanyId(companyId);
        vipLevel.setEntity(true);
        List<VipLevel> vipLevelList = commonService.searchAll(vipLevel);


        Comparator<VipLevel> comparator = new Comparator<VipLevel>() {
            @Override
            public int compare(VipLevel o1, VipLevel o2) {
                if (o1.getStartScore() == null) {
                    return -1;
                }

                if (o2.getStartScore() == null) {
                    return -1;
                }

                if (o1.getEndScore() == null) {
                    return 1;
                }

                if (o2.getEndScore() == null) {
                    return 1;
                }

                return o1.getEndScore() - o2.getEndScore();
            }

            @Override
            public boolean equals(Object obj) {
                return false;
            }
        };

        return vipLevelList.stream().sorted(comparator).collect(Collectors.toList());
    }

    /**
     * 过滤会员等级
     *
     * @param vipLevelList
     * @param levelId
     * @return
     */
    public Optional<VipLevel> filterVipLevel(List<VipLevel> vipLevelList, Long levelId) {
        if (isEmpty(vipLevelList)) {
            return Optional.empty();
        }

        return vipLevelList.stream().filter(p -> equals(p.getId(), levelId)).findAny();
    }

    /**
     * 查询公司的VIP等级
     *
     * @param companyId
     * @return
     */
    public VipLevel findVipLevel_company_vip(Long companyId) throws Exception {
        VerifyUtil.verify(() -> companyId != null);
        VipLevel vipLevel = new VipLevel();
        vipLevel.setCompanyId(companyId);
        vipLevel.setEntity(true);
        vipLevel.setVip(true);
        return commonService.searchOne(vipLevel);
    }


    /**
     * 检查当前会员积分等级是否与其它会员积分等级冲突了
     *
     * @param vipLevel
     */
    public void checkVipLevelScore(VipLevel vipLevel) throws Exception {
        VipLevel vipScoreLevel = (VipLevel) ClassUtil.clone(vipLevel);
        int maxValue = Integer.MAX_VALUE;
        if (vipScoreLevel.getStartScore() == null) {
            vipScoreLevel.setStartScore(-maxValue);
        }
        if (vipScoreLevel.getEndScore() == null) {
            vipScoreLevel.setEndScore(maxValue);
        }
        if (vipLevel.getId() == null) {
            //创建
            List<VipLevel> vipLevelList = findVipLevelByCompany(vipLevel.getCompanyId());
            vipLevelList = vipLevelList.stream().filter(p -> !getBoolean(p.getVip())).collect(Collectors.toList());
            for (VipLevel level : vipLevelList) {
                VipLevel vipLevelScoreInDb = (VipLevel) ClassUtil.clone(level);
                if (vipLevelScoreInDb.getEndScore() == null) {
                    vipLevelScoreInDb.setEndScore(maxValue);
                }
                boolean scoreInRange = isScoreNotInRange(vipScoreLevel.getStartScore(), vipScoreLevel.getEndScore(), vipLevelScoreInDb.getStartScore(), vipLevelScoreInDb.getEndScore());
                VerifyUtil.verify(() -> scoreInRange, VIP_LEVEL);
            }
            return;
        } else {
            VipLevel vipLevelInDb = findVipLevel(vipLevel.getId());
            //修改
            List<VipLevel> vipLevelList = findVipLevelByCompany(vipLevelInDb.getCompanyId());
            vipLevelList = vipLevelList.stream()
                    .filter(p -> !getBoolean(p.getVip())).filter(p -> !equals(p.getId(), vipLevel.getId())).
                            collect(Collectors.toList());
            for (VipLevel level : vipLevelList) {
                VipLevel vipLevelScoreInDb = (VipLevel) ClassUtil.clone(level);
                if (vipLevelScoreInDb.getEndScore() == null) {
                    vipLevelScoreInDb.setEndScore(maxValue);
                }
                boolean scoreInRange = isScoreNotInRange(vipScoreLevel.getStartScore(), vipScoreLevel.getEndScore(), vipLevelScoreInDb.getStartScore(), vipLevelScoreInDb.getEndScore());
                VerifyUtil.verify(() -> scoreInRange, VIP_LEVEL);
            }
            return;
        }
    }

    /**
     * 判断积分是否在值中间
     * 左闭右开
     *
     * @return
     */

    public boolean isScoreNotInRange(int beginScore, int finishScore, int startScore, int endScore) {
        //开始小于结束
        boolean beginLessThanEnd = beginScore <= finishScore;
        boolean compareStart = beginScore >= startScore && beginScore < endScore;
        boolean compareEnd = finishScore > startScore && finishScore <= endScore;
        boolean forward = !compareStart && !compareEnd && beginLessThanEnd;
        //反向
        boolean reverseCompareStart = startScore >= beginScore && startScore < finishScore;
        boolean reverseCompareEnd1 = endScore > beginScore && endScore <= finishScore;
        boolean reverse = !reverseCompareStart && !reverseCompareEnd1;
        return forward && reverse;
    }

    /**
     * 查询会员等级
     *
     * @param vipLevelId
     * @return
     */
    public Optional<VipLevel> findCustomerVipLevel(Long vipLevelId) throws Exception {
        return commonService.searchOneOptional(vipLevelId, VipLevel.class);
    }

    /**
     * 查询会员等级
     *
     * @param companyId
     * @return
     */
    public Optional<VipLevel> findCustomerVipLevelByCompanyId(Long companyId) {
        VipLevel vipLevel = new VipLevel();
        vipLevel.setVip(true);
        vipLevel.setCompanyId(companyId);
        return commonService.searchOneOptional(vipLevel);
    }

    /**
     * 查询会员等级
     *
     * @param customer
     * @return
     */
    public Optional<VipLevel> findCustomerVipLevel(Customer customer) throws Exception {
        if (customer.getVipLevelId() != null) {
            Optional<VipLevel> customerVipLevel = findCustomerVipLevel(customer.getVipLevelId());
            if (customerVipLevel.isPresent()) {
                return customerVipLevel;
            }
        }

        //根据积分查询
        Optional<VipLevel> vipLevelOptional = findCustomerVipLevel(customer.getScoreHistory(), customer.getCompanyId());
        return vipLevelOptional;
    }

    /**
     * 添加积分给会员
     *
     * @param order
     */
    public void addScoreToCustomer(Order order) throws Exception {
        try {
            if (order.getCustomerId() == null) {
                logger.info("order not bind the customer");
                return;
            }

            Long customerId = order.getCustomerId();

            Optional<Customer> customerOptional = findCustomerOptional(customerId);

            if (!customerOptional.isPresent()) {
                logger.info("can not find the customer {}", customerId);
                return;
            }

            Customer customer = customerOptional.get();
            //查询餐厅的积分配置
            Optional<VipLevel> vipLevelOptional = findCustomerVipLevel(customer);

            //通常是会员等级被删除了找不到
            if (!vipLevelOptional.isPresent()) {
                //添加消费历史记录
                addCustomerOrderHistory(order, 0);
                logger.info("can not find the vip level for {}", customer.getVipLevelId());
                return;
            }

            VipLevel vipLevel = vipLevelOptional.get();
            logger.info("find the vip level is {}", vipLevel.getId());

            //再看会员功能是否开启
            Long companyId = vipLevel.getCompanyId();
            Company company = companyService.findCompanyById(companyId);

            if (!getBoolean(company.getCompanyConfig().getCustomerOpen())) {
                logger.info("customer open is close for {}", companyId);
                return;
            }

            //按照消费的金额来算积分
            if (getInt(vipLevel.getConsumeByCost()) == VIP_LEVEL_SCORE_EXCHANGE_TYPE_CASH) {
                double userCost = getDouble(order.getUserCost());

                if (userCost < 0) {
                    logger.info("order user cost less than 0");
                    //添加消费历史记录
                    addCustomerOrderHistory(order, 0);
                    return;
                }

                if (getInt(vipLevel.getUserCost()) <= 0) {
                    //添加消费历史记录
                    addCustomerOrderHistory(order, 0);
                    logger.info("vip level user cost less than 0");
                    return;
                }

                //几倍的值
                int userCostTime = (int) (userCost / vipLevel.getUserCost());
                //user cost score 允许为0，那么就是简直的加消费历史记录
                int score = userCostTime * getInt(vipLevel.getUserCostScore());
                int customerScore = getInt(customer.getScore());
                //添加历史记录
                customer.setScoreHistory(getInt(customer.getScoreHistory()) + score);
                //加上这一次的积分
                customer.setScore(customerScore + score);
                commonService.save(customer);

                logger.info("add score history");
                //添加积分历史记录
                addScoreHistory(customerId, SCORE_HISTORY_TYPE_ADD, score, order.getId(), order.getShopId());

                //添加消费历史记录
                addCustomerOrderHistory(order, score);
            } else {
                //按照消费的次数来算积分
                int orderNumber = getInt(customer.getOrderNumber());
                int costTime = getInt(vipLevel.getCostTime());
                if (costTime < 0) {
                    //添加消费历史记录
                    addCustomerOrderHistory(order, 0);
                    logger.info("can not deal cost time less than 0");
                    return;
                }

                //以前消费的历史记录加上现在的次数，超过了定义的次数，那么可以增加积分
                if ((orderNumber + 1) >= costTime) {
                    int customerScore = getInt(customer.getScore());
                    int costTimeScore = getInt(vipLevel.getCostTimeScore());

                    //定义的积分小于等于0，不做处理
                    if (costTimeScore <= 0) {
                        //添加消费历史记录
                        addCustomerOrderHistory(order, 0);
                        logger.info("cost time score less equals than 0");
                        return;
                    }

                    logger.info("add score history");

                    customer.setScoreHistory(getInt(customer.getScoreHistory()) + costTimeScore);
                    customer.setScore(customerScore + costTimeScore);
                    commonService.save(customer);

                    //添加积分历史记录
                    addScoreHistory(customerId, SCORE_HISTORY_TYPE_ADD, costTimeScore, order.getId(), order.getShopId());

                    //添加消费历史记录
                    addCustomerOrderHistory(order, costTimeScore);
                }

            }

            //处理会员的等级
            setCustomerVipLevel(customer);
            //处理会员等级相关的优惠券
            addLevelTicketToCustomer(customer);
        } catch (Exception e) {
            //会员系统处理异常不影响正常流程
            logger.error("e", e);
        }
    }

    /**
     * 查询会员的历史订单
     *
     * @param customerId
     * @return
     */
    public List<CustomerOrder> findCustomerOrder(Long customerId) {
        if (customerId == null) {
            return new ArrayList<>();
        }

        CustomerOrder customerOrder = new CustomerOrder();
        customerOrder.setCustomerId(customerId);
        customerOrder.setSortField(new String[]{"createTime"});
        customerOrder.setSortType("DESC");

        return commonService.searchList(customerOrder);
    }

    /**
     * 处理订单里面的会员信息
     *
     * @param order
     */
    public void dealCustomerDataInOrder(Order order) throws Exception {
        Long customerId = order.getCustomerId();
        //没有会员的数据
        if (customerId == null) {
            return;
        }

        Customer customer = findCustomer(customerId);
        if (customer == null) {
            logger.info("can not find the customer");
            return;
        }

        //设置年龄
        Integer age = getAge(customer);
        if (age != null) {
            if (age < 20) {
                order.setAge(10);
            } else if (age < 30) {
                order.setAge(20);
            } else if (age < 40) {
                order.setAge(30);
            } else {
                order.setAge(40);
            }
        }

        //设置性别
        order.setSex(customer.getSex());

        //设置新客，还是熟客
        List<CustomerOrder> customerOrderList = findCustomerOrder(customerId);
        //没有下过订单，算是新客人
        order.setNewCustomerOrder(isEmpty(customerOrderList) ? 0 : 1);

        //添加会员等级
        if (customer.getVipLevelId() != null) {
            order.setCustomerLevel(((Number) (customer.getVipLevelId())).intValue());
        }

        //是否是VIP会员消费
        if (getBoolean(customer.getVip()) && getLong(customer.getVipEndTime()) > System.currentTimeMillis()) {
            order.setCustomerVip(true);
        }
    }

    /**
     * 获取年龄
     *
     * @param customer
     * @return
     */
    public Integer getAge(Customer customer) {
        if (customer == null) {
            return null;
        }

        if (customer.getBirthDay() == null) {
            return null;
        }

        int year = DateUtil.getYear(customer.getBirthDay());
        int thisYear = DateUtil.getYear(System.currentTimeMillis());

        if (year >= thisYear) {
            logger.info("year data wrong, year {}, thisYear {}", year, thisYear);
            return null;
        }

        return thisYear - year;
    }

    /**
     * 处理会员等级
     *
     * @param customer
     */
    public void setCustomerVipLevel(Customer customer) throws Exception {
        Integer scoreHistory = getInt(customer.getScoreHistory());

        //如果找到了积分在哪一个区间，那么处理等级，没有找到，那么等级不变
        Long companyId = customer.getCompanyId();

        Optional<VipLevel> levelOptional = findCustomerVipLevel(scoreHistory, companyId);
        if (!levelOptional.isPresent()) {
            logger.info("can not find the score level {}", scoreHistory);
            return;
        }

        VipLevel vipLevelInDb = levelOptional.get();
        customer.setVipLevelId(vipLevelInDb.getId());
        commonService.save(customer);
    }

    /**
     * 获取用户的等级级别
     *
     * @param scoreHistory
     */
    public Optional<VipLevel> findCustomerVipLevel(int scoreHistory, Long companyId) throws ResponseStatusException {
        if (companyId == null) {
            return Optional.empty();
        }

        //如果找到了积分在哪一个区间，那么处理等级，没有找到，那么等级不变
        List<VipLevel> vipLevelList = findVipLevelByCompany(companyId);

        Optional<VipLevel> vipLevelOptional = vipLevelList.stream().filter(p -> {
            return !getBoolean(p.getVip()) && isNumberInRange(scoreHistory, p.getStartScore(), p.getEndScore()) && getBoolean(p.getEnable());
        }).findAny();

        if (vipLevelOptional.isPresent()) {
            return vipLevelOptional;
        } else {
            logger.info("can not find the vip level, add the latest level");
            List<VipLevel> filterLevelList = vipLevelList.stream().filter(p -> {
                return getInt(p.getEndScore()) <= scoreHistory && getBoolean(p.getEnable() && !getBoolean(p.getVip()));
            }).collect(Collectors.toList());

            if (isEmpty(filterLevelList)) {
                logger.info("filter level list empty");
                return Optional.empty();
            }

            VipLevel vipLevel = filterLevelList.get(filterLevelList.size() - 1);

            return Optional.of(vipLevel);
        }
    }

    /**
     * 添加积分变化的历史记录
     *
     * @param customerId
     * @param changeType
     * @param value
     * @param orderId
     */
    public void addScoreHistory(Long customerId, Integer changeType, Integer value, Long orderId, Long shopId) throws Exception {
        //添加积分的消费记录，必要要是大于0
        if (changeType == SCORE_HISTORY_TYPE_ADD && value <= 0) {
            logger.info("history type add value is {}", value);
            return;
        }

        ScoreHistory scoreHistory = new ScoreHistory();
        scoreHistory.setCustomerId(customerId);
        scoreHistory.setChangeType(changeType);
        scoreHistory.setValue(value);
        scoreHistory.setOrderId(orderId);
        scoreHistory.setShopId(shopId);
        commonService.createObject(scoreHistory);
    }

    /**
     * @param order
     */
    public void addCustomerOrderHistory(Order order, int score) throws Exception {
        if (order.getCustomerId() == null) {
            logger.info("order not bind the customer");
            return;
        }

        Long customerId = order.getCustomerId();

        Optional<Customer> customerOptional = findCustomerOptional(customerId);

        if (!customerOptional.isPresent()) {
            logger.info("can not find the customer {}", customerId);
            return;
        }

        logger.info("user will add score is {} ", score);

        CustomerOrder customerOrder = new CustomerOrder();
        customerOrder.setCustomerId(customerId);
        customerOrder.setScore(score);
        customerOrder.setUserCost(showMoneyDouble(order.getUserCost()));
        customerOrder.setOrderId(order.getParentOrderId());
        customerOrder.setPayOrderId(order.getId());
        customerOrder.setShopId(order.getShopId());

        //设置分店的名称
        ShopService shopService = ShopService.getInstance();
        Shop shop = shopService.findShopById(order.getShopId());
        customerOrder.setShopName_zh(shopService.getShopNameZh(shop));
        customerOrder.setShopName_en(shopService.getShopNameEn(shop));

        //设置订单的种类
        int orderType = getInt(order.getOrderType());
        if (orderType == ORDER_TYPE_TANG_SHI || orderType == ORDER_TYPE_QUICK_FOOD) {
            customerOrder.setOrderType(1);
        } else if (orderType == ORDER_TYPE_TAKE_OUT) {
            customerOrder.setOrderType(2);
        } else {
            customerOrder.setOrderType(2);
        }

        commonService.createObject(customerOrder);
    }

    /**
     * 查询积分商城
     *
     * @param scoreShopId
     * @return
     */
    public Optional<ScoreShop> findScoreShop(Long scoreShopId) throws Exception {
        return commonService.searchOneOptional(scoreShopId, ScoreShop.class);
    }

    /**
     * 生成二维码的图片，并上传到云端，返回值即是云端的地址
     *
     * @param qrCode
     * @return
     */
    public void generateQrCode(String qrCode) {
        File qrCodeFile = ImgUtil.createQrCode(qrCode);
        ObjectService.getInstance().uploadObject(qrCodeFile);
    }

    /**
     * 判断数字是否在范围内
     *
     * @param checkNumber
     * @param startNumber
     * @param endNumber
     */
    public boolean isNumberInRange(Integer checkNumber, Integer startNumber, Integer endNumber) {
        if (startNumber != null && endNumber != null) {
            return checkNumber >= startNumber && checkNumber < endNumber;
        }

        if (getInt(startNumber) == 0 && endNumber != null) {
            return checkNumber < endNumber;
        }

        return checkNumber > getInt(endNumber);
    }

    /**
     * 添加返回优惠券给会员账号
     *
     * @param realDiscountTicket 真实有code的优惠券
     * @param customerId
     * @param limitNumber        是否限制数量
     */
    public void addTicketToCustomer(DiscountTicket realDiscountTicket, Long customerId, Long orderId, Boolean limitNumber, VipLevelTicketConfig levelTicketConfig) throws Exception {
        CustomerTicket customerTicket = new CustomerTicket();
        customerTicket.setCompanyId(realDiscountTicket.getCompanyId());

        if (getBoolean(realDiscountTicket.getAssignByExcel())) {
            customerTicket.setAssignByExcel(true);
        }

        if (levelTicketConfig != null) {
            customerTicket.setLevelTicketId(levelTicketConfig.getId());
            customerTicket.setLevelId(levelTicketConfig.getLevelId());
            customerTicket.setVip(levelTicketConfig.getVip());
        }

        customerTicket.setCustomerId(customerId);
        customerTicket.setTicketId(realDiscountTicket.getId());
        customerTicket.setParentTicketId(realDiscountTicket.getParentId());
        customerTicket.setUsed(false);
        customerTicket.setExpiredTime(realDiscountTicket.getEndTime());
        customerTicket.setExpiredDate(DateUtil.formatDate(realDiscountTicket.getEndTime()));
        customerTicket.setLimitNumber(limitNumber);
        customerTicket.setExpired(false);
        customerTicket.setOrderId(orderId);
        customerTicket.setCode(realDiscountTicket.getCode());

        commonService.createObject(customerTicket);
    }

    /**
     * 查询出会员里面没有过期的，并且没有使用的优惠券列表
     *
     * @param customerId
     * @return
     */
    public List<DiscountTicket> findCustomerTicketList(Long customerId) {
        CustomerTicket customerTicket = new CustomerTicket();
        customerTicket.setCustomerId(customerId);
        customerTicket.setUsed(false);

        DiscountTicketService discountTicketService = DiscountTicketService.getInstance();

        List<CustomerTicket> customerTicketList = commonService.searchAll(customerTicket);
        return customerTicketList.stream().map(p -> {
            Long ticketId = p.getTicketId();
            try {
                return discountTicketService.findTicket(ticketId, true);
            } catch (Exception e) {
                logger.error("e", e);
                return null;
            }
        }).filter(p -> p != null).filter(p -> getLong(p.getEndTime()) > System.currentTimeMillis()).collect(Collectors.toList());
    }

    /**
     * 查询店长的优惠券
     *
     * @param companyId
     * @return
     */
    public List<DiscountTicket> findWaiterLeaderTicketList(Long companyId) {
        DiscountTicket discountTicket = new DiscountTicket();
        discountTicket.setCompanyId(companyId);
        discountTicket.setCustomerId(WAITER_LEADER_TICKET);

        return commonService.searchAll(discountTicket);
    }

    /**
     * 查询优惠券
     *
     * @param customerId
     * @param code
     * @return
     */
    public Optional<CustomerTicket> findCustomerTicket(Long customerId, String code) {
        CustomerTicket customerTicket = new CustomerTicket();
        customerTicket.setCustomerId(customerId);
        customerTicket.setCode(code);
        return commonService.searchOneOptional(customerTicket);
    }

    /**
     * 查询优惠券
     *
     * @param companyId
     * @param code
     * @return
     */
    public Optional<CustomerTicket> findCustomerTicketInCompany(Long companyId, String code) {
        CustomerTicket customerTicket = new CustomerTicket();
        customerTicket.setCompanyId(companyId);
        customerTicket.setCode(code);
        return commonService.searchOneOptional(customerTicket);
    }

    /**
     * 判断优惠券是否满足订单
     *
     * @param discountTicket
     * @param order
     * @return
     */
    public boolean ticketSatisfyOrder(DiscountTicket discountTicket, Order order) {
        logger.info("check ticket satisfy {}", discountTicket.getId());
        Double atLeastCost = getDouble(discountTicket.getAtLeastCost());
        Double costOrigin = getDouble(order.getCostOrigin());
        boolean satisfyCondition = (long) (costOrigin * 100) >= (long) (atLeastCost * 100);
        if (!satisfyCondition) {
            return false;
        }

        //正常的优惠券的使用处理
        if (!DiscountTicketService.getInstance().isWaiterLeaderTicket(discountTicket)) {
            //判断分店是否正确
            List<NameAndId> shopList = discountTicket.getShopList();
            Optional<NameAndId> nameAndIdOptional = shopList.stream().filter(p -> equals(p.getId(), order.getShopId())).findAny();
            if (!nameAndIdOptional.isPresent()) {
                logger.info("can not find the proper shop");
                return false;
            }

            //判断优惠券的场景
            Optional<NameAndId> sceneOptional = getList(discountTicket.getSceneList()).stream().filter(p -> equals(p.getId(), order.getSceneId())).findAny();
            if (!sceneOptional.isPresent()) {
                logger.info("can not find the proper scene");
                return false;
            }

            //过期的优惠券不能使用
            if (getLong(discountTicket.getEndTime()) < System.currentTimeMillis()) {
                logger.info("ticket expired ");
                return false;
            }

            //已经使用过的优惠券，不能再被使用
            if (getBoolean(discountTicket.getUsed())) {
                logger.info("ticket used");
                return false;
            }

            //判断是否过期了
            if (getBoolean(discountTicket.getExpired())) {
                logger.info("ticket expired");
                return false;
            }

            //没到可使用时间的，不能使用
            if (discountTicket.getStartTime() > System.currentTimeMillis()) {
                logger.info("startTime not arrived");
                return false;
            }

        }

        //要求是堂食的订单，却是外卖的订单
        if (!isOrderTypeMatchTicket(order, discountTicket)) {
            return false;
        }

        //如果是菜品券，那么要判断订单里面的菜是否包含在里面
        if (getBoolean(discountTicket.getItemTicket())) {
            List<OrderItem> filterOrderItemList = OrderService.getInstance().filterOrderItem(order, discountTicket);
            if (isEmpty(filterOrderItemList)) {
                logger.info("no item match the ticket {}", discountTicket.getId());
                return false;
            }
        }

        discountTicket.setCanUse(true);
        return true;
    }

    /**
     * 如果使用的优惠券是不限量的优惠券，那么新产生一张优惠券，再绑定到会员账号上面
     *
     * @param order
     */
    public void dealCustomerTicket(Order order) throws Exception {
        if (order.getCustomerId() == null) {
            logger.info("customer Id is empty");
            return;
        }

        List<String> codeList = null;

        OrderService orderService = OrderService.getInstance();

        codeList = orderService.getOrderTicketCodeList(order);

        if (isEmpty(codeList)) {
            logger.info("code list is empty~");
            return;
        }

        Long customerId = order.getCustomerId();
        Customer customer = CustomerService.getInstance().findCustomer(customerId);
        Long companyId = order.getCompanyId();
        DiscountTicketService discountTicketService = DiscountTicketService.getInstance();

        for (String code : codeList) {
            Optional<CustomerTicket> customerTicketOptional = findCustomerTicket(customerId, code);
            if (!customerTicketOptional.isPresent()) {
                logger.info("{} is not exist", code);
                continue;
            }

            CustomerTicket customerTicket = customerTicketOptional.get();

            //将会员的优惠券设置成已使用
            customerTicket.setUsed(true);
            commonService.save(customerTicket);
            logger.info("set customer ticket to used {}", customerTicket.getId());

            if (getBoolean(customerTicket.getLimitNumber())) {
                logger.info("limit the number");
                continue;
            }

            VipLevelTicketConfig vipLevelTicketConfig = null;

            //VIP券，添加特殊的判断条件
            if (getBoolean(customerTicket.getVip())) {
                //VIP送的券，判断是否还是VIP，以及VIP是否过期了
                if (!customer.getVip()) {
                    logger.info("user is not vip");
                    continue;
                }

                //vip已经过期了
                if (getLong(customer.getVipEndTime()) < System.currentTimeMillis()) {
                    logger.info("customer vip end time is {}, date {}", customer.getVipEndTime(), DateUtil.formatDate(getLong(customer.getVipEndTime())));
                    continue;
                }

                //添加VIP的等级
                Optional<VipLevelTicketConfig> vipLevelTicketConfigOptional = findVipTicketConfigById(customerTicket.getLevelTicketId());
                if (!vipLevelTicketConfigOptional.isPresent()) {
                    logger.info("the config is delete");
                    continue;
                }

                vipLevelTicketConfig = vipLevelTicketConfigOptional.get();
            } else {
                Long levelTicketId = customerTicket.getLevelTicketId();
                if (levelTicketId != null) {
                    Optional<VipLevelTicketConfig> vipLevelTicketConfigOptional = findVipTicketConfigById(levelTicketId);
                    if (!vipLevelTicketConfigOptional.isPresent()) {
                        logger.info("the level ticket config deleted");
                        continue;
                    }

                    vipLevelTicketConfig = vipLevelTicketConfigOptional.get();

                    //当前会员的等级已经变了，也不再送了
                    if (!equals(customer.getVipLevelId(), vipLevelTicketConfig.getLevelId())) {
                        logger.info("the level changed customer now level {}, ticket config level is {}", customer.getVipLevelId(), vipLevelTicketConfig.getLevelId());
                        continue;
                    }
                }
            }

            DiscountTicket discountTicket = discountTicketService.findTicketByCode(code, companyId);
            Long parentTicketId = discountTicket.getParentId();
            if (parentTicketId == null) {
                logger.info("can not find the parent ticket {}", discountTicket.getId());
                continue;
            }

            DiscountTicket parentTicket = discountTicketService.findTicket(discountTicket.getId());

            //已经过期了，那么不再派券
            if (System.currentTimeMillis() > parentTicket.getEndTime()) {
                logger.info("ticket expired {}", parentTicket.getId());
                continue;
            }

            //创建新的优惠券给用户
            DiscountTicket realTicket = discountTicketService.createRealTicket(parentTicket);
            addTicketToCustomer(realTicket, customerId, order.id, false, vipLevelTicketConfig);
        }
    }

    /**
     * 数据导出
     *
     * @param customer
     * @return
     */
    public File exportData(Customer customer) throws Exception {
        Long count = commonService.count(customer);
        VerifyUtil.verify(() -> count <= 10000, EXPORT_DATA_TOO_LARGE);
        List<Customer> customerList = commonService.searchAll(customer);

        Long companyId = customer.getCompanyId();
        List<VipLevel> levelList = findVipLevelByCompany(companyId);

        //将reportList转化成二维数组
        List<List<Object>> dataList = customerList.stream().map(c -> {
            List<Object> objectList = new ArrayList<>();
            objectList.add(c.getPhone());
            objectList.add(c.getAreaCode());

            objectList.add(c.getEmail());
            objectList.add(c.getNickName());

            if (getInt(c.getSex()) == 1) {
                objectList.add("男/male");
            } else if (getInt(c.getSex()) == 2) {
                objectList.add("女/female");
            } else {
                objectList.add("");
            }

            //生日
            objectList.add(getLong(c.getBirthDay()) == 0 ? "" : DateUtil.formatDayDate(c.getBirthDay()));
            //偏好设置
            List<Integer> preferenceList = c.getPreferenceList();
            if (isEmpty(preferenceList)) {
                objectList.add("");
            } else {
                List<String> list = preferenceList.stream().map(p -> String.format("%s/%s", PREFERENCE_MAP_ZH.get(p), PREFERENCE_MAP_EN.get(p))).collect(Collectors.toList());
                objectList.add(StringUtils.join(list, ","));
            }

            //VIP
            if (getBoolean(c.getVip())) {
                objectList.add("YES");
                objectList.add(DateUtil.formatDayDate(c.getVipEndTime()));
            } else {
                objectList.add("NO");
                objectList.add("");
            }

            //积分
            objectList.add(getInt(c.getScore()));

            //等级
            Optional<VipLevel> levelOptional = filterVipLevel(levelList, c.getVipLevelId());
            if (!levelOptional.isPresent()) {
                objectList.add("");
            } else {
                VipLevel vipLevel = levelOptional.get();
                objectList.add(String.format("%s/%s", vipLevel.getName_zh(), vipLevel.getName_en()));
            }

            //状态
            if (getInt(c.getStatus()) == 0) {
                objectList.add("正常/normal");
            } else {
                objectList.add("冻结/frozen");
            }

            objectList.add(c.getCreateDate());

            return objectList;
        }).collect(Collectors.toList());

        //报表数据，转化成excel数据
        List<String> titleList = Arrays.asList("電話號碼/phone", "區號/Area Code", "郵箱/Email", "昵稱/Nick Name",
                "性别/Sex", "生日/Birthday", "偏好/Preference", "VIP",
                "VIP結束時間/VIP End Time", "積分/Score", "會員等級/Level", "狀態/Status", "註冊時間/Register Time");
        File file = ExcelUtil.getInstance().exportExcel(titleList, dataList);

        //将文件上传到指定的桶
        ObjectService.getInstance().uploadObject(file);

        return file;
    }

    /**
     * 判断会员是否合法
     *
     * @param customer
     */
    public void isCustomerValid(Customer customer) throws ResponseStatusException {
        VerifyUtil.verify(() -> getInt(customer.getStatus()) == 0, CUSTOMER_FROZEN);
    }

    /**
     * 判断主菜是否存在
     *
     * @param companyId 公司ID
     * @return
     */
    public Optional<CompanyMainItem> isCompanyMainItemExist(Long companyId, String name, int itemType) {
        CompanyMainItem findCompanyMainItem = new CompanyMainItem();
        findCompanyMainItem.setCompanyId(companyId);
        findCompanyMainItem.setName_zh_en(name);
        findCompanyMainItem.setItemType(itemType);
        Optional<CompanyMainItem> companyMainItemOptional = commonService.searchOneOptional(findCompanyMainItem);
        return companyMainItemOptional;
    }

    /**
     * 从现有的一个主菜里面获取或者创建一个新的主菜
     *
     * @param companyId
     * @param name
     * @return
     */
    public CompanyMainItem getOrCreateCompanyMainItem(Long companyId, String name, Integer itemType) throws Exception {
        Optional<CompanyMainItem> companyMainItemExist = isCompanyMainItemExist(companyId, name, itemType);
        if (companyMainItemExist.isPresent()) {
            return companyMainItemExist.get();
        }

        CompanyMainItem companyMainItem = new CompanyMainItem();
        companyMainItem.setCompanyId(companyId);
        companyMainItem.setName(name);
        companyMainItem.setName_zh(name);
        companyMainItem.setName_en(name);
        companyMainItem.setItemType(itemType);
        commonService.createObject(companyMainItem);
        return companyMainItem;
    }

    /**
     * 查询续费VIP模板
     *
     * @param companyId
     * @return
     */
    public Optional<VipRenewEmailTemplate> findVipRenewEmailTemplateByCompany(Long companyId) {
        VipRenewEmailTemplate vipRenewEmailTemplateQuery = new VipRenewEmailTemplate();
        vipRenewEmailTemplateQuery.setCompanyId(companyId);
        Optional<VipRenewEmailTemplate> vipRenewEmailTemplateOptional = commonService.searchOneOptional(vipRenewEmailTemplateQuery);

        return vipRenewEmailTemplateOptional;
    }

    /**
     * 查询生日邮件模板
     * 如果不存在，则创建一个
     *
     * @param companyId
     * @return
     */
    public MarketBirthdayTemplate findBirthdayTemplate(Long companyId) throws Exception {
        MarketBirthdayTemplate marketBirthdayTemplate = new MarketBirthdayTemplate();
        marketBirthdayTemplate.setCompanyId(companyId);
        Optional<MarketBirthdayTemplate> marketBirthdayTemplateOptional = commonService.searchOneOptional(marketBirthdayTemplate);

        if (!marketBirthdayTemplateOptional.isPresent()) {
            MarketBirthdayTemplate marketBirthdayTemplateNew = new MarketBirthdayTemplate();
            marketBirthdayTemplateNew.setCompanyId(companyId);
            commonService.createObject(marketBirthdayTemplateNew);
            return findBirthdayTemplate(companyId);
        }

        MarketBirthdayTemplate marketBirthdayTemplateInDb = marketBirthdayTemplateOptional.get();

        if (marketBirthdayTemplateInDb != null) {
            //过滤不存在的优惠券
            List<Long> ticketIdList = getList(marketBirthdayTemplateInDb.getTicketIdList()).stream().filter(ticketId -> {
                DiscountTicket ticket = null;
                try {
                    ticket = DiscountTicketService.getInstance().findTicket(ticketId);
                    return ticket != null;
                } catch (Exception e) {
                    return false;
                }
            }).collect(Collectors.toList());

            marketBirthdayTemplateInDb.setTicketIdList(ticketIdList);
            commonService.save(marketBirthdayTemplateInDb);
        }

        return marketBirthdayTemplateInDb;
    }

    /**
     * 营销任务
     *
     * @param marketTask
     */
    public void dealMarketTaskDefaultValue(MarketTask marketTask) throws Exception {
        List<TicketAndLimit> ticketAndLimitList = marketTask.getTicketAndLimitList();
        if (isNotEmpty(ticketAndLimitList)) {
            for (TicketAndLimit ticketAndLimit : ticketAndLimitList) {
                VerifyUtil.verify(() -> ticketAndLimit.getTicketId() != null);
                commonService.dealDefaultValueWithAnnotation(ticketAndLimit);
            }
        }
    }

    /**
     * 将查询条件转化成真实的会员对象列表
     *
     * @param userSearchCondition
     * @return
     */
    public List<Long> findAllCustomerIdListByQueryCondition(String userSearchCondition) {
        Customer customer = JSON.parseObject(userSearchCondition, Customer.class);
        List<Long> customerIdList = commonService.getAndConvertAll(customer, (c) -> {
            return c.getId();
        });

        return customerIdList;
    }

    /**
     * 查询满足条件的邮件个数
     *
     * @param userSearchCondition
     * @return
     */
    public Long countEmailByQueryCondition(String userSearchCondition) {
        Customer customer = JSON.parseObject(userSearchCondition, Customer.class);
        List<String> emailList = commonService.getAndConvertAll(customer, (c) -> {
            return c.getEmail();
        });

        return emailList.stream().filter(email -> isNotEmpty(email)).count();
    }

    /**
     * 查询满足条件的电话号码个数
     *
     * @param userSearchCondition
     * @return
     */
    public long countPhoneByQueryCondition(String userSearchCondition) {
        Customer customer = JSON.parseObject(userSearchCondition, Customer.class);
        List<String> phoneList = commonService.getAndConvertAll(customer, (c) -> {
            return c.getPhone();
        });

        return phoneList.stream().filter(email -> isNotEmpty(email)).count();
    }

    /**
     * 获取发送邮件名称
     *
     * @param company
     * @return
     */
    public String getSenderName(Company company) {
        if (company == null) {
            return null;
        }

        if (company.getCompanyConfig() == null) {
            return null;
        }

        String sendNameInEmail = company.getCompanyConfig().getSendNameInEmail();
        return sendNameInEmail;
    }

    /**
     * 发送营销任务
     *
     * @param marketTask
     */
    public void executeMarketTask(MarketTask marketTask) throws Exception {
        List<Long> customerIdList = getList(marketTask.getCustomerIdList());
        Long companyId = marketTask.getCompanyId();
        Company company = companyService.findCompanyById(companyId);
        String senderName = getSenderName(company);

        String smsSignature = String.format("[%s]", company.getName());

        MarketHistory marketHistory = new MarketHistory();
        marketHistory.setTaskName_zh(marketTask.getTaskName_zh());
        marketHistory.setTaskName_en(marketTask.getTaskName_en());
        marketHistory.setCompanyId(marketTask.getCompanyId());
        marketHistory.setTaskId(marketTask.getId());

        Optional<Quota> quotaOptional = findQuotaOrCreate(companyId);
        VerifyUtil.verify(() -> quotaOptional.isPresent());
        Quota quota = quotaOptional.get();

        CustomerEmailClient customerEmailClient = CustomerEmailClient.getInstance();

        List<DiscountTicket> templateTicketList = getList(marketTask.getTicketAndLimitList()).stream().map(p -> {
            try {
                return DiscountTicketService.getInstance().findTicket(p.getTicketId());
            } catch (Exception e) {
                return null;
            }
        }).filter(p -> p != null).collect(Collectors.toList());
        for (Long customerId : customerIdList) {
            try {
                boolean sendEmail = false;
                boolean sendSMS = false;
                boolean addTicket = false;

                //查询历史任务
                List<TaskExecuteHistory> taskExecuteHistoryList = null;

                {
                    TaskExecuteHistory taskExecuteHistory = new TaskExecuteHistory();
                    taskExecuteHistory.setCustomerId(customerId);
                    taskExecuteHistory.setTaskType(CUSTOMER_TASK_TYPE_MARKET_TASK);
                    taskExecuteHistory.setTaskId(marketTask.getId());
                    taskExecuteHistory.setSortField(new String[]{"createTime"});
                    taskExecuteHistory.setSortType("DESC");
                    taskExecuteHistoryList = commonService.searchList(taskExecuteHistory);
                }

                if (!canExecuteTask(taskExecuteHistoryList, marketTask.getPeriodTimeConfig())) {
                    logger.info("customer {} maybe execute ", customerId);
                    continue;
                }

                logger.info("will execute the market task {}", marketTask.getId());

                Customer customer = findCustomer(customerId);

                //处理配额
                int emailCanUsed = quota.getEmailCanUsed();

                //邮件配额足够
                if (emailCanUsed > 0) {
                    if (getBoolean(marketTask.getEmail()) && isNotEmpty(customer.getEmail())) {
                        customerEmailClient.sendEmail(senderName, customer.getEmail(), marketTask.getEmailTitle(), marketTask.getEmailContent(), customer);
                        addEmailSendHistory(companyId, true);

                        //减少配额
                        quota.setEmailCanUsed(emailCanUsed - 1);
                        quota.setEmailUsed(getInt(quota.getEmailUsed()) + 1);
                        commonService.save(quota);

                        //添加邮件接收人数
                        Integer emailCount = getInt(marketHistory.getEmailCount());
                        marketHistory.setEmailCount(emailCount + 1);

                        sendEmail = true;
                    }
                }

                int smsCanUsed = getInt(quota.getSmsCanUsed());

                //判断所有的优惠券加起来需要多少短信，如果满足，才依次发送，如果不满足，则不发送
                int smsSum = templateTicketList.stream().map(ticket -> getSmsContent(marketTask, ticket, smsSignature)).mapToInt(content -> calSmsLength(content)).sum();

                if (smsCanUsed >= smsSum) {
                    //短信的配额足够
                    //有几张优惠券发送几次
                    if (getBoolean(marketTask.getSms()) && isNotEmpty(customer.getPhone()) && marketTask.getSmsId() != null) {
                        for (DiscountTicket discountTicket : templateTicketList) {
                            String smsContent = getSmsContent(marketTask, discountTicket, smsSignature);

                            AliyunSMSClient.getInstance().send(customer.getAreaCode(), customer.getPhone(), smsContent);

                            int smsLength = calSmsLength(smsContent);
                            addSmsSendHistory(companyId, true, smsLength);

                            //减少配额
                            quota.setSmsCanUsed(getInt(quota.getSmsCanUsed()) - smsLength);
                            quota.setSmsUsed(getInt(quota.getSmsUsed()) + smsLength);
                            commonService.save(quota);
                        }

                        //添加短信人数
                        Integer phoneCount = getInt(marketHistory.getPhoneCount());
                        //计算需要的短信条数，再相加
                        marketHistory.setPhoneCount(phoneCount + smsSum);

                        sendSMS = true;
                    }
                }

                //如果选择了短信或者邮件，至少要有一个发送了，那么可以发送优惠券
                //如果短信或者邮件都没有选择的时候，可以处理
                if (((getBoolean(marketTask.getEmail()) || getBoolean(marketTask.getSms()) && (sendEmail || sendSMS)))
                        || (!getBoolean(marketTask.getEmail()) && !getBoolean(marketTask.getSms()))
                ) {
                    //发送优惠券
                    List<TicketAndLimit> ticketList = getList(marketTask.getTicketAndLimitList());
                    DiscountTicketService discountTicketService = DiscountTicketService.getInstance();

                    for (TicketAndLimit ticketAndLimit : ticketList) {
                        Long ticketId = ticketAndLimit.getTicketId();
                        Optional<DiscountTicket> ticketOptional = discountTicketService.findTicketOptional(ticketId);
                        if (!ticketOptional.isPresent()) {
                            continue;
                        }


                        addTicket = true;

                        //发送几张
                        Integer ticketNumber = ticketAndLimit.getTicketNumber();
                        if (getInt(ticketNumber) <= 0) {
                            ticketNumber = 1;
                        }

                        for (int i = 0; i < ticketNumber; i++) {
                            //优惠券，还存在，发送
                            DiscountTicket realTicket = discountTicketService.createRealTicket(ticketOptional.get());
                            addTicketToCustomer(realTicket, customerId, null, getBoolean(ticketAndLimit.getLimitNumber()), null);
                        }
                    }
                }

                //添加执行任务历史记录
                {
                    TaskExecuteHistory taskExecuteHistory = new TaskExecuteHistory();
                    taskExecuteHistory.setCustomerId(customerId);
                    taskExecuteHistory.setTaskType(CUSTOMER_TASK_TYPE_MARKET_TASK);
                    taskExecuteHistory.setTaskId(marketTask.getId());
                    commonService.createObject(taskExecuteHistory);
                }

                //要么发送了邮件，要么发送了短信，要么添加了优惠券，才新增人数
                if (sendEmail || sendSMS || addTicket) {
                    Integer personCount = getInt(marketHistory.getPersonCount());
                    //添加实际收件人数
                    marketHistory.setPersonCount(personCount + 1);
                }

            } catch (Exception e) {
                logger.error("e", e);
                continue;
            }
        }

        //保存历史记录，至少要人数大于1才保存,也就是要么发送了邮件，或者短信，或者优惠券
        if (getInt(marketHistory.getPersonCount()) > 0) {
            commonService.directSave(marketHistory);
        }

        //添加执行次数
        Integer executeTime = getInt(marketTask.getExecuteTime());

        marketTask.setExecuteTime(executeTime + getInt(marketHistory.getPersonCount()));
        marketTask.setEmailSendCount(getInt(marketHistory.getEmailCount()) + getInt(marketTask.getEmailSendCount()));
        marketTask.setPhoneSendCount(getInt(marketHistory.getPhoneCount()) + getInt(marketTask.getPhoneSendCount()));
        commonService.save(marketTask);
    }

    /**
     * 是否是一次性券
     *
     * @param periodTimeConfig
     * @return
     */
    public boolean isOnceTimeTicket(PeriodTimeConfig periodTimeConfig) {
        if (periodTimeConfig == null) {
            return false;
        }

        return getBoolean(periodTimeConfig.getOnce());
    }

    /**
     * 将VIP的优惠券添加给会员
     *
     * @param customerInDb
     */
    private void addVipTicketToCustomer_vip(Customer customerInDb) throws Exception {
        if (customerInDb == null) {
            logger.info("customer is null");
            return;
        }

        //不是VIP的账号
        if (!getBoolean(customerInDb.getVip())) {
            logger.info("customer is not vip");
            return;
        }

        long currentTimeMillis = System.currentTimeMillis();

        //会员的VIP时间已经超了，也不送
        if (currentTimeMillis > getLong(customerInDb.getVipEndTime())) {
            logger.info("customer end time is expired");
            return;
        }

        //开始送券
        Long companyId = customerInDb.getCompanyId();
        if (companyId == null) {
            logger.info("company id is null");
            return;
        }

        VipLevel vipLevel = findVipLevel_company_vip(companyId);
        if (vipLevel == null) {
            logger.info("vip level is null, {}", companyId);
            return;
        }

        Long customerId = customerInDb.getId();
        if (customerId == null) {
            logger.info("customer id is null");
            return;
        }

        addLevelTicketToCustomer(vipLevel.getId(), customerId);
    }

    /**
     * 判断会员等级的定期券是否可以使用
     *
     * @param vipLevel
     */
    public boolean isPeriodLevelTimeOk(VipLevel vipLevel) {
        if (vipLevel == null || vipLevel.getId() == null) {
            return false;
        }

        Long vipLevelId = vipLevel.getId();

        List<VipLevelTicketConfig> vipTicketConfig = findVipTicketConfig(vipLevelId);

        List<VipLevelTicketConfig> filterLevelTicketConfig = vipTicketConfig.stream().filter(config -> {
            PeriodTimeConfig periodTimeConfig = config.getPeriodTimeConfig();
            if (periodTimeConfig == null) {
                return false;
            }

            return !getBoolean(periodTimeConfig.getOnce());
        }).collect(Collectors.toList());

        if (isEmpty(filterLevelTicketConfig)) {
            logger.info("filter level ticket config is empty");
            return false;
        }

        filterLevelTicketConfig = filterLevelTicketConfig.stream().filter(p -> {
            try {
                return DiscountTicketService.getInstance().findTicketOptional(p.getTicketId()).isPresent();
            } catch (Exception exception) {
                return false;
            }
        }).collect(Collectors.toList());

        if (isEmpty(filterLevelTicketConfig)) {
            logger.info("filter level ticket config is empty");
            return false;
        }


        return false;
    }

    /**
     * 等级的优惠券是否在可以用的时间范围内
     *
     * @param levelTicketConfig
     * @return
     */
    public boolean isLevelTicketTimeOk(VipLevelTicketConfig levelTicketConfig) throws Exception {
        if (levelTicketConfig == null) {
            return false;
        }

        PeriodTimeConfig periodTimeConfig = levelTicketConfig.getPeriodTimeConfig();

        int periodType = getInt(periodTimeConfig.getPeriodType());
        if (periodType == PERIOD_TYPE_YEAR) {
            int month = getInt(periodTimeConfig.getMonth());
            int day = getInt(periodTimeConfig.getDay());

            if (!CustomerTaskService.getInstance().isPeriodTimeValid(periodTimeConfig)) {
                logger.info("period time is not valid");
                return false;
            }

            int currentMonth = DateUtil.getCurrentMonth();
            int currentDay = DateUtil.getCurrentDay();

            //月不相同，肯定不行
            if (month != currentMonth) {
                return false;
            }

            //特殊情况，如果是2月份，设置的28号，29号，均按照28号来处理
            if (month == 2) {
                if (Arrays.asList(28, 29).contains(day)) {
                    if (currentDay == 28) {
                        return true;
                    } else {
                        return false;
                    }
                }
            }

            //剩下的天数也要相等
            return currentDay == day;
        } else if (periodType == PERIOD_TYPE_MONTH) {
            int day = getInt(periodTimeConfig.getDay());

            if (!CustomerTaskService.getInstance().isPeriodTimeValid(periodTimeConfig)) {
                logger.info("period time is not valid");
                return false;
            }

            int currentDay = DateUtil.getCurrentDay();

            //如果日期比设置的要大，那么同意发送
            if (currentDay >= day) {
                return true;
            }

            //如果小了，那么要看是否是月底
            if (day == 31 || day == 30) {
                //月底发送，主要是针对2月份
                if (DateUtil.isLastDayOfMonth(new Date())) {
                    return true;
                }
            }

            return false;
        } else if (periodType == PERIOD_TYPE_WEEK) {
            int weekDay = getInt(periodTimeConfig.getWeekDay());

            if (!CustomerTaskService.getInstance().isPeriodTimeValid(periodTimeConfig)) {
                logger.info("period time is not valid");
                return false;
            }

            int currentDayOfWeek = DateUtil.getCurrentDayOfWeek();
            return weekDay == currentDayOfWeek;
        } else if (periodType == PERIOD_TYPE_DAY) {
            int month = getInt(periodTimeConfig.getMonth());
            int day = getInt(periodTimeConfig.getDay());

            if (!CustomerTaskService.getInstance().isPeriodTimeValid(periodTimeConfig)) {
                logger.info("period time is not valid");
                return false;
            }

            int currentMonth = DateUtil.getCurrentMonth();
            int currentDay = DateUtil.getCurrentDay();

            //月不相同，肯定不行
            if (month != currentMonth) {
                return false;
            }

            //特殊情况，如果是2月份，设置的28号，29号，均按照28号来处理
            if (month == 2) {
                if (Arrays.asList(28, 29).contains(day)) {
                    if (currentDay == 28) {
                        return true;
                    } else {
                        return false;
                    }
                }
            }

            //剩下的天数也要相等
            return currentDay == day;
        } else {
            logger.info("period type is {}, return false", periodType);
            return false;
        }
    }

    /**
     * 查找会员的会员等级，然后查找该等级的所有一次性优惠券,如果这些优惠券没有发过，那么需要发送
     *
     * @param customerInDb
     */
    public void addLevelTicketToCustomer(Customer customerInDb) throws Exception {
        if (customerInDb == null) {
            logger.info("customer is null");
            return;
        }

        Long levelId = customerInDb.getVipLevelId();
        if (levelId == null) {
            logger.info("customer {} , level id null", customerInDb.getId());
            return;
        }

        Optional<VipLevel> vipLevelOptional = commonService.searchOneOptional(levelId, VipLevel.class);
        if (!vipLevelOptional.isPresent()) {
            logger.info("can not find the level");
            return;
        }

        //会员等级关闭之后不再送优惠券
        if (!getBoolean(vipLevelOptional.get().getEnable())) {
            logger.info("can not assign ticket");
            return;
        }

        Long customerId = customerInDb.getId();
        if (customerId == null) {
            logger.info("can not get the customer ID");
            return;
        }

        addLevelTicketToCustomer(levelId, customerId);

        //如果还是VIP，那么添加VIP的优惠券
        if (getBoolean(customerInDb.getVip()) && getLong(customerInDb.getVipEndTime()) > System.currentTimeMillis()) {
            addVipTicketToCustomer_vip(customerInDb);
        }
    }

    /**
     * 将某一个等级的优惠券送给会员
     *
     * @param levelId
     * @param customerId
     * @throws Exception
     */
    public void addLevelTicketToCustomer(Long levelId, Long customerId) throws Exception {
        Customer customer = findCustomer(customerId);

        if (customer == null) {
            logger.info("can not find the customer {}", customerId);
            return;
        }

        //会员状态不正常，不用送
        if (getInt(customer.getStatus()) != 0) {
            logger.info("customer {} status {}", customerId, customer.getStatus());
            return;
        }

        VipLevelTicketConfig vipLevelTicketConfig = new VipLevelTicketConfig();
        vipLevelTicketConfig.setLevelId(levelId);
        List<VipLevelTicketConfig> vipLevelTicketConfigList = commonService.searchAll(vipLevelTicketConfig);

        //将这里的优惠券都发送给会员，只发送未发送过的优惠券
        for (VipLevelTicketConfig levelTicketConfig : vipLevelTicketConfigList) {
            TaskExecuteHistory taskExecuteHistory = new TaskExecuteHistory();
            taskExecuteHistory.setCustomerId(customerId);
            taskExecuteHistory.setLevelTicketId(levelTicketConfig.getId());
            taskExecuteHistory.setTaskType(CUSTOMER_TASK_TYPE_LEVEL_TICKET);
            taskExecuteHistory.setSortField(new String[]{"createTime"});
            taskExecuteHistory.setSortType("DESC");

            Page<TaskExecuteHistory> taskExecuteHistoryPage = commonService.search(taskExecuteHistory);

            List<TaskExecuteHistory> taskExecuteHistoryList = taskExecuteHistoryPage.getContent();

            //過期的不送
            Long ticketId = levelTicketConfig.getTicketId();
            DiscountTicket ticketTemplate = DiscountTicketService.getInstance().findTicket(ticketId);
            if (ticketTemplate.getExpired()) {
                logger.info("ticket {} expired", ticketTemplate.getId());
                continue;
            }


            if (!canExecuteTask(taskExecuteHistoryList, levelTicketConfig.getPeriodTimeConfig())) {
                logger.info("the task execute {}", levelTicketConfig.getId());
                continue;
            }

            //送几张
            int ticketNumber = 1;
            if (getInt(levelTicketConfig.getTicketNumber()) >= 1) {
                ticketNumber = levelTicketConfig.getTicketNumber();
            }

            for (int i = 0; i < ticketNumber; i++) {
                DiscountTicketService discountTicketService = DiscountTicketService.getInstance();
                Optional<DiscountTicket> discountTicketOptional = discountTicketService.findTicketOptional(levelTicketConfig.getTicketId());

                if (discountTicketOptional.isPresent()) {
                    DiscountTicket discountTicket = discountTicketOptional.get();
                    DiscountTicket realTicket = discountTicketService.createRealTicket(discountTicket);
                    addTicketToCustomer(realTicket, customerId, null, levelTicketConfig.getLimitNumber(), levelTicketConfig);
                }
            }

            //添加写定期券的日志记录
            {
                TaskExecuteHistory taskExecuteHistoryNewOne = new TaskExecuteHistory();
                taskExecuteHistoryNewOne.setCustomerId(customerId);
                taskExecuteHistoryNewOne.setLevelTicketId(levelTicketConfig.getId());
                taskExecuteHistoryNewOne.setTaskType(CUSTOMER_TASK_TYPE_LEVEL_TICKET);
                commonService.createObject(taskExecuteHistoryNewOne);
            }
        }
    }

    /**
     * 根据时间配置以及历史执行纪录来判断当前是否可以执行周期性的任务
     *
     * @param taskExecuteHistoryList
     * @param periodTimeConfig
     * @return
     */
    public <T extends AbstractEntity> boolean canExecuteTask(List<T> taskExecuteHistoryList, PeriodTimeConfig periodTimeConfig) {
        if (periodTimeConfig == null) {
            logger.info("period time config is null");
            return false;
        }

        //一次性的券，从来没有执行过
        if (getBoolean(periodTimeConfig.getOnce())) {
            return isEmpty(taskExecuteHistoryList);
        }

        Integer periodType = getInt(periodTimeConfig.getPeriodType());
        switch (periodType) {
            case PERIOD_TYPE_DAY: {
                List<AbstractEntity> filterTaskList = taskExecuteHistoryList.stream().filter(p -> {
                    long currentTimeMillis = System.currentTimeMillis();
                    String today = DateUtil.formatDayDate(currentTimeMillis);
                    Long createTime = p.getCreateTime();
                    String executeDate = DateUtil.formatDayDate(createTime);
                    return equals(today, executeDate);
                }).collect(Collectors.toList());

                logger.info("daily task");

                //当天没有执行过，那么需要执行,执行过了，也就不需要执行了
                return filterTaskList.isEmpty();
            }
            case PERIOD_TYPE_WEEK: {
                //本周内的这一天没有执行，那么可以执行
                List<AbstractEntity> filterTaskList = taskExecuteHistoryList.stream().filter(p -> {
                    int weekOfYear = DateUtil.getWeekOfYear(System.currentTimeMillis());
                    Long createTime = p.getCreateTime();
                    int executeWeekOfYear = DateUtil.getWeekOfYear(createTime);

                    int thisYear = DateUtil.getYear(System.currentTimeMillis());
                    int exeYear = DateUtil.getYear(createTime);

                    return executeWeekOfYear == weekOfYear && thisYear == exeYear;
                }).collect(Collectors.toList());

                logger.info("week task");

                //本周执行过了，那么不能执行了
                if (isNotEmpty(filterTaskList)) {
                    return false;
                }

                //没有执行过，那么需要判断时间对得上吗
                Integer weekDay = periodTimeConfig.getWeekDay();
                int dayOfWeek = 0;
                try {
                    dayOfWeek = DateUtil.getDayOfWeek(System.currentTimeMillis());
                } catch (Exception e) {
                    //有执行不能执行
                    logger.error("e", e);
                    return false;
                }

                return weekDay == dayOfWeek;
            }
            case PERIOD_TYPE_MONTH: {
                if (periodTimeConfig.getDay() == null) {
                    logger.info("time config day is null");
                    return false;
                }

                logger.info("month task");

                //本周内的这一天没有执行，那么可以执行
                List<AbstractEntity> filterTaskList = taskExecuteHistoryList.stream().filter(p -> {
                    int currentMonth = DateUtil.getMonth(System.currentTimeMillis());
                    Long createTime = p.getCreateTime();
                    int executeMonth = DateUtil.getMonth(createTime);

                    int year = DateUtil.getYear(System.currentTimeMillis());
                    int exeYear = DateUtil.getYear(createTime);

                    return currentMonth == executeMonth && year == exeYear;
                }).collect(Collectors.toList());

                //执行过了，那么不再执行
                if (isNotEmpty(filterTaskList)) {
                    return false;
                }

                //判断当前的时间是否相等，相等肯定可以发
                int today = DateUtil.getDay(System.currentTimeMillis());
                int exeDay = periodTimeConfig.getDay();
                if (today == exeDay) {
                    return true;
                }

                //不是相同的一天，有几种可能
                //设置的是31号，当前月是30号，月底要执行
                //如果设置的30号，那么对应的天数在30号的时候会执行，31号的时候不执行
                //如果设置是31，30号，在2月的时候，有可能是28，29号，也是月底需要执行
                try {
                    return DateUtil.isLastDayOfMonth(DateUtil.formatDayDate(System.currentTimeMillis()));
                } catch (ParseException e) {
                    logger.error("e", e);
                    return false;
                }
            }
            case PERIOD_TYPE_YEAR: {
                int month = getInt(periodTimeConfig.getMonth());
                int day = getInt(periodTimeConfig.getDay());

                if (!CustomerTaskService.getInstance().isPeriodTimeValid(periodTimeConfig)) {
                    logger.info("period time is not valid");
                    return false;
                }

                logger.info("year task");

                //如果曾经执行过了，那么不需要再执行了
                List<AbstractEntity> filterTaskList = taskExecuteHistoryList.stream().filter(p -> {
                    int thisYear = DateUtil.getYear(System.currentTimeMillis());
                    int exeYear = DateUtil.getYear(p.getCreateTime());
                    return thisYear == exeYear;
                }).collect(Collectors.toList());

                if (isNotEmpty(filterTaskList)) {
                    return false;
                }

                int currentMonth = DateUtil.getCurrentMonth();
                int currentDay = DateUtil.getCurrentDay();

                //月不相同，肯定不行
                if (month != currentMonth) {
                    return false;
                }

                //特殊情况，如果是2月份，设置的28号，29号，均按照28号来处理
                if (month == 2) {
                    if (Arrays.asList(28, 29).contains(day)) {
                        if (currentDay == 28) {
                            return true;
                        } else {
                            return false;
                        }
                    }
                }

                //剩下的天数也要相等
                return currentDay == day;
            }
            default: {
                logger.info("periodType is {}", periodType);
                return false;
            }
        }
    }


    /**
     * 会员修改自己的密码
     *
     * @param customer
     */
    public void customerModifySelfPassword(Customer customer, Customer customerInDb) throws Exception {
        String oldPassword = customer.getOldPassword();
        String salt = customerInDb.getSalt();
        String passwordInDb = Sha256Util.sha256(oldPassword, salt);
        VerifyUtil.verify(() -> equals(customerInDb.getPassword(), passwordInDb), PASSWORD_WRONG);

        //新密码
        String newSalt = Sha256Util.generateSalt();
        customerInDb.setSalt(newSalt);
        String newPassword = Sha256Util.sha256(customer.getPassword(), newSalt);
        customerInDb.setPassword(newPassword);
        commonService.save(customerInDb);
    }

    /**
     * 查询会员的某一个消息历史
     *
     * @param customerId
     * @param orderId
     */
    public Optional<ScoreHistory> findScoreHistory(Long customerId, Long shopId, Long orderId) {
        logger.info("will find the customerId {}, shopId {}, orderId {}", customerId, shopId, orderId);
        ScoreHistory scoreHistory = new ScoreHistory();
        scoreHistory.setCustomerId(customerId);
        scoreHistory.setShopId(shopId);
        scoreHistory.setOrderId(orderId);
        return commonService.findOneOptional(scoreHistory);
    }

    /**
     * 查询
     *
     * @param code
     * @param companyId
     * @return
     */
    public Optional<CustomerTicket> findCustomerTicket(String code, Long companyId) {
        CustomerTicket customerTicket = new CustomerTicket();
        customerTicket.setCompanyId(companyId);
        customerTicket.setCode(code);
        return commonService.searchOneOptional(customerTicket);
    }

    /**
     * 查询对应等级的优惠券
     *
     * @param levelId
     * @return
     */
    public List<VipLevelTicketConfig> findVipTicketConfig(Long levelId) {
        VipLevelTicketConfig vipLevelTicketConfig = new VipLevelTicketConfig();
        vipLevelTicketConfig.setLevelId(levelId);
        return commonService.searchAll(vipLevelTicketConfig);
    }

    /**
     * 查询某一个等级的优惠券
     *
     * @param id
     * @return
     * @throws Exception
     */
    public Optional<VipLevelTicketConfig> findVipTicketConfigById(Long id) throws Exception {
        return commonService.searchOneOptional(id, VipLevelTicketConfig.class);
    }

    /**
     * 订单类型是否与优惠券适用的类型匹配
     *
     * @param order
     * @param discountTicket
     * @return
     */
    public boolean isOrderTypeMatchTicket(Order order, DiscountTicket discountTicket) {
        //判断外卖，还是堂食
        OrderService orderService = OrderService.getInstance();
        boolean tangShiOrder = orderService.isTangOrder(order);

        if (tangShiOrder && getBoolean(discountTicket.getTangshi())) {
            return true;
        }

        if (!tangShiOrder && getBoolean(discountTicket.getTakeout())) {
            return true;
        }

        logger.info("tanshiOrder {}, discount ticket tangshi {} takeout", tangShiOrder, discountTicket.getTangshi(), discountTicket.getTakeout());

        return false;
    }

    /**
     * 删除会员优惠券
     *
     * @param companyId
     * @param parentTicketId
     */
    public void deleteCustomerTicket(Long companyId, Long parentTicketId) {
        try {
            CustomerTicket customerTicket = new CustomerTicket();
            customerTicket.setParentTicketId(parentTicketId);
            customerTicket.setBeRequest(true);
            commonService.delete(customerTicket, false);
        } catch (Exception e) {
            logger.error("e", e);
        }
    }

    /**
     * 查询会员在某一个等级下面的优惠券列表
     *
     * @param customerId
     * @param levelId
     * @return
     */
    public List<CustomerTicket> findCustomerTicket(Long customerId, Long levelId) {
        CustomerTicket customerTicket = new CustomerTicket();
        customerTicket.setCustomerId(customerId);
        customerTicket.setLevelId(levelId);
        return commonService.searchAll(customerTicket);
    }

    /**
     * 添加會員等級信息
     *
     * @param c
     */
    public void addCustomerLevelInfo(Customer c) {
        if (c == null) {
            return;
        }

        Long level = c.getVipLevelId();
        if (level != null) {
            try {
                Optional<VipLevel> vipLevelOption = findVipLevelOption(level);
                if (vipLevelOption.isPresent()) {
                    c.setLevelName_zh(vipLevelOption.get().getName_zh());
                    c.setLevelName_en(vipLevelOption.get().getName_en());
                }
            } catch (Exception e) {
                logger.error("e", e);
            }

        }
    }

    /**
     * 根据查询条件来获取所有的会员ID
     *
     * @param customer
     * @return
     */
    public Set<Long> findCustomerIdList(Customer customer) throws Exception {
        Customer cloneCustomer = (Customer) ClassUtil.clone(customer);

        List<Long> idList = commonService.getAndConvertAll(cloneCustomer, (customer1 -> {
            return customer1.getId();
        }));

        return new HashSet<Long>(idList);
    }

    /**
     * 查询邮件存在的人数
     *
     * @param customer
     * @return
     * @throws Exception
     */
    public Set<Long> findCustomerIdList_emailExist(Customer customer) throws Exception {
        Customer cloneCustomer = (Customer) ClassUtil.clone(customer);
        cloneCustomer.setEmailExist(true);

        List<Long> idList = commonService.getAndConvertAll(cloneCustomer, (customer1 -> {
            return customer1.getId();
        }));

        return new HashSet<Long>(idList);
    }

    /**
     * 查询邮件存在的人数
     *
     * @param customer
     * @return
     * @throws Exception
     */
    public Set<Long> findCustomerIdList_phoneExist(Customer customer) throws Exception {
        Customer cloneCustomer = (Customer) ClassUtil.clone(customer);
        cloneCustomer.setPhoneExist(true);

        List<Long> idList = commonService.getAndConvertAll(cloneCustomer, (customer1 -> {
            return customer1.getId();
        }));

        return new HashSet<Long>(idList);
    }

    /**
     * 计算实际人数
     *
     * @param marketTask
     * @return
     */
    public Map<String, Object> calTaskUser(MarketTask marketTask) throws Exception {
        Set<Long> idSets = new HashSet<>();
        Set<Long> emailIdSets = new HashSet<>();
        Set<Long> phoneIdSets = new HashSet<>();
        Long totalNumber = 0l;

        List<Customer> customerList = marketTask.getCustomerList();
        for (Customer customer : customerList) {
            Set<Long> customerIdList = findCustomerIdList(customer);

            totalNumber = totalNumber + customerIdList.size();
            idSets.addAll(customerIdList);

            emailIdSets.addAll(findCustomerIdList_emailExist(customer));
            phoneIdSets.addAll(findCustomerIdList_phoneExist(customer));
        }


        HashMap<String, Object> resultMap = new HashMap<>();
        resultMap.put("totalNumber", totalNumber);
        resultMap.put("realNumber", idSets.size());
        resultMap.put("emailNumber", emailIdSets.size());
        resultMap.put("phoneNumber", phoneIdSets.size());
        resultMap.put("customerIdList", idSets);

        return resultMap;
    }

    /**
     * 查询营销任务
     *
     * @param id
     * @return
     */
    public Optional<MarketTask> findMarketTask(Long id) {
        MarketTask marketTask = new MarketTask();
        marketTask.setId(id);
        return commonService.findOneOptional(marketTask);
    }

    /**
     * 查询公司的营销任务
     *
     * @param companyId
     * @return
     */
    public List<MarketTask> findMarketTaskByCompanyId(Long companyId) {
        MarketTask marketTask = new MarketTask();
        marketTask.setCompanyId(companyId);
        return commonService.searchAll(marketTask);
    }

    /**
     * 获取优惠券的有效的开始时间
     *
     * @param discountTicket
     * @return
     */
    public Long getTicketStartValidTime(DiscountTicket discountTicket) {
        //没有兑换后的时间
        if (discountTicket.getBindingDays() == null) {
            return discountTicket.getStartTime();
        } else {
            //有兑换后的时间，那么当天就是有效时间的开始
            return System.currentTimeMillis();
        }
    }

    /**
     * 获取优惠券的有效的结束时间
     *
     * @param discountTicket
     * @return
     */
    public Long getTicketEndValidTime(DiscountTicket discountTicket) {
        //没有兑换后的时间
        if (discountTicket.getBindingDays() == null) {
            return discountTicket.getEndTime();
        } else {
            //有兑换后的时间，那么当天就是有效时间的结束
            return DateUtil.endTimeInDay(System.currentTimeMillis() + discountTicket.getBindingDays() * DateUtil.ONE_DAY);
        }
    }

    /**
     * 添加邮件的发送历史
     *
     * @param companyId
     * @param plan
     */
    public void addEmailSendHistory(Long companyId, boolean plan) throws Exception {
        SendHistory sendHistory = new SendHistory();
        sendHistory.setCompanyId(companyId);
        sendHistory.setEmail(true);
        sendHistory.setSms(false);
        sendHistory.setPlan(plan);
        sendHistory.setSendNumber(1);
        commonService.directSave(sendHistory);
    }

    /**
     * 添加短信的发送历史
     *
     * @param companyId
     * @param plan
     * @throws Exception
     */
    public void addSmsSendHistory(Long companyId, boolean plan, int smsLength) throws Exception {
        SendHistory sendHistory = new SendHistory();
        sendHistory.setCompanyId(companyId);
        sendHistory.setSms(true);
        sendHistory.setEmail(false);
        sendHistory.setPlan(plan);
        sendHistory.setSendNumber(smsLength);
        commonService.directSave(sendHistory);
    }

    /**
     * 计算营销任务需要多少条短信
     *
     * @param marketTask
     */
    public List<SmsCount> calSmsCount(MarketTask marketTask) throws Exception {
        Long companyId = marketTask.getCompanyId();
        Company company = CompanyService.getInstance().findCompanyById(companyId);
        String smsSignature = String.format("[%s]", company.getName());

        List<TicketAndLimit> ticketAndLimitList = marketTask.getTicketAndLimitList();

        List<SmsCount> smsCountList = Arrays.asList(1, 2, 3, 4, 5).stream().map(i -> {
            SmsCount smsCount = new SmsCount();
            smsCount.setSmsType(i.longValue());
            return smsCount;
        }).collect(Collectors.toList());

        for (TicketAndLimit ticketAndLimit : ticketAndLimitList) {
            Long ticketId = ticketAndLimit.getTicketId();
            DiscountTicket discountTicket = DiscountTicketService.getInstance().findTicket(ticketId);
            logger.info("discount ticket is {}", discountTicket);
            //将开始以及结束时间计算出来
            Long startValidTime = getTicketStartValidTime(discountTicket);
            Long endValidTime = getTicketEndValidTime(discountTicket);
            String valid_zh = String.format("%s ~ %s", DateUtil.format(startValidTime, "yyyy-MM-dd"), DateUtil.format(endValidTime, "yyyy-MM-dd"));
            String valid_en = String.format("%s ~ %s", DateUtil.format_en(startValidTime, "dd MMM yyyy"), DateUtil.format_en(endValidTime, "dd MMM yyyy"));

            for (SmsCount smsCount : smsCountList) {
                logger.info("sms type is {}", smsCount.getSmsType());
                String smsContent = null;
                //只有中文
                if (getBoolean(marketTask.getSmsZh()) && !getBoolean(marketTask.getSmsEn())) {
                    smsContent = SMS_TEMPLATE_MAP_ZH.get(smsCount.getSmsType());
                    smsContent = String.format(smsContent, smsSignature, discountTicket.getName_zh(), valid_zh);
                } else if (!getBoolean(marketTask.getSmsZh()) && getBoolean(marketTask.getSmsEn())) {
                    //只有英文
                    smsContent = SMS_TEMPLATE_MAP_EN.get(smsCount.getSmsType());
                    smsContent = String.format(smsContent, smsSignature, discountTicket.getName_en(), valid_en);
                } else {
                    //两种都有
                    smsContent = SMS_TEMPLATE_MAP.get(smsCount.getSmsType());
                    smsContent = String.format(smsContent, smsSignature, discountTicket.getName_zh(), valid_zh, discountTicket.getName_en(), valid_en);
                }

                //计算长度
                int smsLength = calSmsLength(smsContent);
                smsCount.setCount(smsCount.getCount() + smsLength);
            }
        }

        return smsCountList;
    }

    /**
     * 计算短信的条数。
     * 汉字、字母、数字、符号，都按照一个字计算。 短信长度不超过70个字，按照一条短信计费；超过70个字，即为长短信，按照67字/条拆分成多条计费。
     * 例如：短信长度为160个字，将会被分隔成67字、67字、26字三条进行计费。 经过后端的核实，您的短信应该是235字符，所以按照多条计费。
     *
     * @param content
     * @return
     */
    public int calSmsLength(String content) {
        if (isEmpty(content)) {
            logger.info("content is empty");
            return 0;
        }

        int length = content.length();
        if (length <= 70) {
            return 1;
        }

        //刚好是67的整数倍，直接返回
        if (length % 67 == 0) {
            return length / 67;
        }

        //比67的整数倍多一点，多的部分算一条短信
        return length / 67 + 1;
    }

    /**
     * 获取短信的内容
     *
     * @param marketTask
     * @return
     */
    public String getSmsContent(MarketTask marketTask, DiscountTicket discountTicket, String smsSignature) {
        if (marketTask.getSmsId() == null) {
            logger.info("market task sms id is empty");
            return "";
        }

        Long startValidTime = getTicketStartValidTime(discountTicket);
        Long endValidTime = getTicketEndValidTime(discountTicket);
        String valid_zh = String.format("%s ~ %s", DateUtil.format(startValidTime, "yyyy-MM-dd"), DateUtil.format(endValidTime, "yyyy-MM-dd"));
        String valid_en = String.format("%s ~ %s", DateUtil.format_en(startValidTime, "dd MMM yyyy"), DateUtil.format_en(endValidTime, "dd MMM yyyy"));

        Long smsId = marketTask.getSmsId();

        String smsContent = null;

        //只有中文
        if (getBoolean(marketTask.getSmsZh()) && !getBoolean(marketTask.getSmsEn())) {
            smsContent = SMS_TEMPLATE_MAP_ZH.get(smsId);
            smsContent = String.format(smsContent, smsSignature, discountTicket.getName_zh(), valid_zh);
        } else if (!getBoolean(marketTask.getSmsZh()) && getBoolean(marketTask.getSmsEn())) {
            //只有英文
            smsContent = SMS_TEMPLATE_MAP_EN.get(smsId);
            smsContent = String.format(smsContent, smsSignature, discountTicket.getName_en(), valid_en);
        } else {
            //两种都有
            smsContent = SMS_TEMPLATE_MAP.get(smsId);
            smsContent = String.format(smsContent, smsSignature, discountTicket.getName_zh(), valid_zh, discountTicket.getName_en(), valid_en);
        }

        return smsContent;
    }

    /**
     * 一个营销任务发送给一个人发送多少条短信
     *
     * @param marketTask
     * @return
     */
    public int calMarketTaskSmsCount(MarketTask marketTask) {
        try {
            if (marketTask == null) {
                logger.info("market task is empty");
                return 0;
            }

            if (!getBoolean(marketTask.getSms())) {
                logger.info("market task not enable sms");
                return 0;
            }

            List<DiscountTicket> templateTicketList = getList(marketTask.getTicketAndLimitList()).stream().map(p -> {
                try {
                    return DiscountTicketService.getInstance().findTicket(p.getTicketId());
                } catch (Exception e) {
                    return null;
                }
            }).filter(p -> p != null).collect(Collectors.toList());

            if (isEmpty(templateTicketList)) {
                logger.info("ticket list is empty");
                return 0;
            }

            Long companyId = marketTask.getCompanyId();
            if (companyId == null) {
                logger.info("company id is empty");
                return 0;
            }

            Company company = companyService.findCompanyById(companyId);
            String smsSignature = String.format("[%s]", company.getName());
            return templateTicketList.stream().map(ticket -> getSmsContent(marketTask, ticket, smsSignature)).mapToInt(content -> calSmsLength(content)).sum();
        } catch (Exception e) {
            logger.error("e", e);
            return 0;
        }
    }


}
